#include "myarea.h"
#include <cairomm/context.h>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <sstream>

MyArea::MyArea()
	: zoom(0), zoom_level(NULL), cur(Gdk::FLEUR)
{
	set_events(Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK|Gdk::POINTER_MOTION_HINT_MASK|Gdk::POINTER_MOTION_MASK );
	signal_button_press_event().connect(
		sigc::mem_fun(*this, &MyArea::on_eventbox_button_press) );
	signal_button_release_event().connect(
		sigc::mem_fun(*this, &MyArea::on_eventbox_button_release) );
	signal_scroll_event().connect(
		sigc::mem_fun(*this, &MyArea::on_eventbox_scroll) );
	signal_motion_notify_event().connect(
		sigc::mem_fun(*this, &MyArea::on_eventbox_pointer_motion) );
}

MyArea::~MyArea()
{
}

void MyArea::reset_zoom()
{
	zoom = *space;
}

void MyArea::set_tree_view(const Glib::RefPtr<Gtk::TreeSelection>& t)
{
	m_view = t;
	m_view->signal_changed().connect(
		sigc::mem_fun(*this, &MyArea::paint)
	);
}

void MyArea::set_model(const Glib::RefPtr<Gtk::ListStore>& r)
{
	model = r;
}

void MyArea::set_space(Rectangle *s)
{
	space = s;
}

void MyArea::change_zoom()
{
	double h = space->top() - space->bottom();
	double w = space->right() - space->left();
	if(zoom_level)
	{

		double level = zoom_level->get_value();
		zoom.size(h/level,w/level);
//		if(zoom.right() > space->right())
//		{
//			zoom.place(zoom.left() - zoom.right() + space->right(), zoom.bottom());
//		}
//		if(zoom.top() > space->top())
//		{
//			zoom.place(zoom.left(),zoom.bottom()-zoom.top()+space->top());
//		}

		paint();
	}

}

void MyArea::set_adjustment (Gtk::Adjustment &adj)
{
	zoom_level = &adj;
	zoom_level->signal_value_changed().connect(sigc::mem_fun(*this, &MyArea::change_zoom));
}

bool MyArea::on_eventbox_button_press(GdkEventButton* event)
{
	Glib::RefPtr<Gdk::Window> window = get_window();
	if(window)
	{
		Gtk::Allocation allocation = get_allocation();
		const int height = allocation.get_height();
		x1 = zoom.left()+(event->x*double(zoom.top()-zoom.bottom()))/height;
		y1 = zoom.bottom()+(event->y*double(zoom.top()-zoom.bottom()))/height;
		return true;
	}
	return false;
}

bool MyArea::on_eventbox_scroll(GdkEventScroll* event)
{
	Glib::RefPtr<Gdk::Window> window = get_window();
	if(window)
	{
		Gtk::Allocation allocation = get_allocation();
		const int height = allocation.get_height();
		double level = zoom_level->get_value();
		double inc = zoom_level->get_step_increment();
		double max = zoom_level->get_upper();
		double min = zoom_level->get_lower();

		double scf;
		double x = (event->x*double(zoom.top()-zoom.bottom()))/height;
		double y = (event->y*double(zoom.top()-zoom.bottom()))/height;

		switch(event->direction)
		{
			case GDK_SCROLL_UP:
			if(level + inc <= max)
			{
				scf = inc/(level + inc);
				zoom.place(zoom.left()+scf*x, zoom.bottom()+scf*y);
				zoom_level->set_value(level + inc);
			}

			break;
			case GDK_SCROLL_DOWN:
			if(level - inc >= min)
			{
				scf = inc/(level - inc);
				zoom.place(zoom.left()-scf*x, zoom.bottom()-scf*y);
				zoom_level->set_value(level - inc);
			}
			break;

		}
		return true;
	}
	return false;
}

bool MyArea::on_eventbox_button_release(GdkEventButton* event)
{
	Glib::RefPtr<Gdk::Window> window = get_window();
	if(window)
	{
		Gtk::Allocation allocation = get_allocation();
		Gdk::Rectangle r(0, 0, get_allocation().get_width(), get_allocation().get_height());
		const int height = allocation.get_height();
		x2 = zoom.left()+(event->x*double(zoom.top()-zoom.bottom()))/height;
		y2 = zoom.bottom()+(event->y*double(zoom.top()-zoom.bottom()))/height;

		zoom.place(zoom.left()-x2+x1, zoom.bottom()-y2+y1);
		typedef Gtk::TreeModel::Children type_children; //minimise code length.
		type_children children = model->children();

		if(!(x1-x2) && !(y1-y2))
		{
			for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
			{
				Gtk::TreeModel::Row row = *iter;
				double bx1 = row[m_Columns.m_col_x] ;
				double by1 = row[m_Columns.m_col_y] ;
				double bx2 = bx1 + row[m_Columns.m_col_w] ;
				double by2 = by1 + row[m_Columns.m_col_h] ;

				if(bx1 <= x2 && x2 <= bx2 && by1 <= y2 && y2 <= by2)
				{
					m_view->select(iter);
				}
			}
		}

		window->invalidate_rect(r, false);
		return true;
	}
	return false;
}

bool MyArea::on_eventbox_pointer_motion(GdkEventMotion* event)
{
	Glib::RefPtr<Gdk::Window> window = get_window();

	if(window)
	{
		int x, y;
		Gdk::ModifierType m;
		window->get_pointer(x,y,m);
		if(m & Gdk::BUTTON1_MASK)
		{


			Gtk::Allocation allocation = get_allocation();
			Gdk::Rectangle r(0, 0, get_allocation().get_width(), get_allocation().get_height());
			const int height = allocation.get_height();
			x2 = zoom.left()+(x*double(zoom.top()-zoom.bottom()))/height;
			y2 = zoom.bottom()+(y*double(zoom.top()-zoom.bottom()))/height;

			//std::cout<<x2<<" "<<y2<<std::endl;
			zoom.place(zoom.left()-x2+x1, zoom.bottom()-y2+y1);
			window->invalidate_rect(r, false);
			return true;
		}

	}
	return false;
}

void MyArea::paint()
{
	Glib::RefPtr<Gdk::Window> win = get_window();
    if (win)
    {
        Gdk::Rectangle r(0, 0, get_allocation().get_width(),
                get_allocation().get_height());
        win->invalidate_rect(r, false);
    }
}


void MyArea::writeFile(std::string filename, MyArea::FileType t)
{
	int width = space->right()-space->left();
	int height = space->top() - space->bottom();
	switch(t)
	{
		case PNG:
		{
			Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create( Cairo::FORMAT_ARGB32,width,height);
			Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(surface);
			draw(context);
			context->show_page();
			surface->write_to_png(filename);
			break;
		}
		case PDF:
		{
			Cairo::RefPtr<Cairo::PdfSurface> surface = Cairo::PdfSurface::create( filename,width+1,height);
			Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(surface);
			draw(context);
			context->show_page();
			surface->flush();
			break;
		}
		case PS:
		{
			Cairo::RefPtr<Cairo::PsSurface> surface = Cairo::PsSurface::create( filename,width,height);
			Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(surface);
			draw(context);
			context->show_page();
			surface->flush();
			break;
		}
	}



}


void MyArea::draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
	srand( (unsigned)time( NULL ) );
	cr->rectangle(space->left(), space->bottom(),space->right()-space->left(),space->top()-space->bottom());
	cr->set_source_rgb(1.0, 1.0, 1.0);
	cr->fill();

	typedef Gtk::TreeModel::Children type_children; //minimise code length.
	type_children children = model->children();
	for(type_children::iterator iter = children.begin(); iter != children.end(); ++iter)
	{
		Gtk::TreeModel::Row row = *iter;
//		int id = row[m_Columns.m_col_id] ;
		int h = row[m_Columns.m_col_h] ;
		int w = row[m_Columns.m_col_w] ;
		int x = row[m_Columns.m_col_x] ;
		int y = row[m_Columns.m_col_y] ;
		double r = row[m_Columns.m_col_red] ;
		double g = row[m_Columns.m_col_green] ;
		double b = row[m_Columns.m_col_blue] ;

		if(r == 0 && b == 0 && g == 0)
		{
			r = (   (double)rand() / ((double)(RAND_MAX)+(double)(1)) );
			g = (   (double)rand() / ((double)(RAND_MAX)+(double)(1)) );
			b = (   (double)rand() / ((double)(RAND_MAX)+(double)(1)) )/2;
			row[m_Columns.m_col_red] = r;
			row[m_Columns.m_col_blue] = b;
			row[m_Columns.m_col_green] = g;
		}

		cr->rectangle(x, y,w,h);
		cr->set_source_rgba(r,g,b,0.7);
		cr->fill();

	}
}




bool MyArea::on_expose_event(GdkEventExpose* event)
{
	//std::cerr<<zoom<<std::endl;
  // This is where we draw on the window

  Glib::RefPtr<Gdk::Window> window = get_window();
  if(window)
  {
    window->set_cursor(cur);
    Gtk::Allocation allocation = get_allocation();
    const int width = allocation.get_width();
    const int height = allocation.get_height();

    // coordinates for the center of the window
    int xc, yc;
    xc = width / 2;
    yc = height / 2;



    Glib::RefPtr<Gdk::Window> win = get_window();
    Cairo::RefPtr<Cairo::Context> cr(
			new Cairo::Context(gdk_cairo_create(win->gobj()), true));

//    double maxwidth = zoom.right()-zoom.left();
    double maxheight = zoom.top()-zoom.bottom();

	cr->rectangle(0,0,width,height);
	cr->clip();
	cr->rectangle(0,0,width,height);
	cr->set_source_rgb(0.0, 0.0, 0.0);
	cr->fill();
   cr->scale(height/maxheight, height/maxheight);

	cr->translate(-zoom.left(), -zoom.bottom());

	draw(cr);

	Gtk::TreeModel::iterator iter = m_view->get_selected();
	if(iter)
	{
		Gtk::TreeModel::Row row = *iter;
//		int id = row[m_Columns.m_col_id] ;
		int h = row[m_Columns.m_col_h] ;
		int w = row[m_Columns.m_col_w] ;
		int x = row[m_Columns.m_col_x] ;
		int y = row[m_Columns.m_col_y] ;

		cr->rectangle(x,y,w,h);
		cr->set_source_rgba(0,0.1,0.5,1);
		cr->fill();
	}

	cr->show_page();

   // draw the rectangle

    //Draw the outer rectangle
  }
  return true;
}

/*
  void paint(cairo_surface_t *cs,int **a,int n)
	{
	cairo_t *c;
	int i=0;
	double r,g,b;

	c=cairo_create(cs);
	srand( (unsigned)time( NULL ) );

	cairo_rectangle(c, 0.0, 0.0, sizex, sizey);
	cairo_set_source_rgb(c, 1.0, 1.0, 1.0);
	cairo_fill(c);
	i=0;
	while(i<n)
	{
		cairo_rectangle(c,a[i][0],a[i][1],a[i][2]-a[i][0],a[i][3]-a[i][1]);
		r = (   (double)rand() / ((double)(RAND_MAX)+(double)(1)) );
		g = (   (double)rand() / ((double)(RAND_MAX)+(double)(1)) );
		b = (   (double)rand() / ((double)(RAND_MAX)+(double)(1)) )/2;
		cairo_set_source_rgba(c,r,g,b,0.5);
		cairo_fill(c);
		cairo_move_to(c,(a[i][0] + a[i][2])/2,(a[i][1]+a[i][3])/2);
		cairo_set_source_rgb(c, 0.0, 0.0, 0.0);
		cairo_show_text(c,name[i]);
		i++;
	}
	cairo_set_line_width(c,0.5);
	cairo_stroke(c);
	cairo_show_page(c);
	cairo_destroy(c);
}*/
